%%
%% %CopyrightBegin%
%%
%% Copyright Hillside Technology Ltd. 2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%

%%% SOAP handler for Inets, the standard HTTP erlang HTTP server. 
%%%
%%% To start, use a server generated by soap:wsdl2erlang:
%%% > soap:start_server(<server>, [{http_server, soap_server_inets}]).

-module(soap_server_inets).

-include_lib("inets/include/httpd.hrl").

-export([start/1, start/2, stop/0]).

%% The call back functions as defined by Inets
-export([do/1]).
-export([store/2]).

start(Module) ->
    start(Module, []).

start(Module, Options) ->
    Port = proplists:get_value(port, Options, 8080),
    inets:start(),
    inets:start(httpd, [{port, Port}, 
        {server_name,"soap_server"}, {server_root,"."},
        {document_root,"."},
        {ipfamily, inet}, %% IP version 4
        {modules, [?MODULE]},
        {soap_handler, Module},
        {soap_options, Options}]).

stop() ->
    inets:stop().

%% store some information at start up: 
%% - the handler module
%% - the options
%% This is retrieved and used with every request.
store({soap_handler, Handler}, _) when is_atom(Handler) ->
    {ok, {soap_handler, Handler}};
store({soap_handler, _Handler}, _) ->
    {error, "Soap handler must be a module (atom)"};
store({soap_options, Options}, _) ->
    {ok, {soap_options, Options}}.

%% handle a request.
do(#mod{config_db = Db} = Req) ->
    [{_, Handler}] = ets:lookup(Db, soap_handler),
    Options = 
        case ets:lookup(Db, soap_options) of
            [{_, Options_value}] -> Options_value;
                _ -> []
        end,
    Handler_resp = 
        case soap_server_handler:new_req(Handler, httpd, Options, Req) of
            {continue, Soap_req} ->
                check_conformance(Req, Soap_req);
            Other ->
                Other
        end,
    {ok, StatusCode, Headers, Resp_body, _Req2} = Handler_resp,
    %% Note: the documentation of httpd is very unclear, but the code shows
    %% that the http status code must be in het list of headers.
    Resp_body_binary = iolist_to_binary(Resp_body),
    Content_length = integer_to_list(size(Resp_body_binary)),
    {proceed, [{response, {response, [{code, StatusCode}, 
               {content_length, Content_length}] ++ Headers, 
               [Resp_body_binary]}}]}.

check_conformance(#mod{entity_body = Req_body} = Req, Soap_req) ->
    %% collect some information about the protocol, so that 
    %% conformance can be checked.
    Soap_req2 = enrich_req(Req, Soap_req),
    case soap_server_handler:check_http_conformance(Soap_req2) of
        {continue, Soap_req3} ->
            Message = list_to_binary(Req_body),
            Soap_req4 = soap_req:set_http_body(Soap_req3, Message),
            soap_server_handler:handle_message(Message, Soap_req4);
        Error ->
            Error
    end.

enrich_req(#mod{method = Method, parsed_header = Headers}, Soap_req) ->
    Soap_req2 = soap_req:set_method(Soap_req, Method),
    Content_type = proplists:get_value("content-type", Headers),
    Soap_req3 = soap_req:set_content_type(Soap_req2, Content_type),
    Soap_action = proplists:get_value("soapaction", Headers),
    soap_req:set_soap_action(Soap_req3, Soap_action).
